package github.ziwukun.jsonql.exe;

import java.util.LinkedList;
import java.util.List;

import github.ziwukun.jsonql.lang.JSONQL;
import github.ziwukun.jsonql.lang.Limit;
import github.ziwukun.jsonql.lang.PropSort;
import github.ziwukun.jsonql.lang.Select;
import github.ziwukun.jsonql.lang.SelectBuilder;
import github.ziwukun.jsonql.lang.SelectItem;
import github.ziwukun.jsonql.lang.Sort;
import github.ziwukun.jsonql.lang.Where;
import github.ziwukun.jsonql.orm.CtnProp;
import github.ziwukun.jsonql.orm.FieldProp;
import github.ziwukun.jsonql.utils.CollectionUtils;

class JQLUtils {
	
	
	/**
	 * 把属性名称的QL转换成字段名称的QL
	 * @return
	 */
	public static JSONQL ctnPropJQL2CtnJQL(String ctnName,
		List<? extends FieldProp> props,JSONQL jql) {
		
		Select select = modelCtnSelect(props,jql.select());
		String from = modelCtnFrom(ctnName,jql);
		Where where = modelCtnWhere(props,jql);
		Limit limit = modelCtnLimit(ctnName,props,jql);
		Sort sort = modelCtnSort(props,jql);
		
		return JSONQL.newInstance(select, from, where,limit,sort);
	}
	
	/**
	 * 把属性名称的Where转换成字段名称的Where
	 * @param props
	 * @param jql
	 * @return
	 */
	public static Where ctnPropJWhere2CtnFieldJWhere(List<? extends FieldProp> props,
		JSONQL jql) {
		return modelCtnWhere(props,jql);
	}
	
	/**
	 * 把属性名称的select语句转换成字段的select语句
	 * @return
	 */
	public static Select ctnPropJSelect2CtnFieldJSelect(
		List<? extends FieldProp> props,Select select) {
		return modelCtnSelect(props,select);
	}
	
	// 构建模型直接归属的容器的select语句
	private static Select modelCtnSelect(List<? extends FieldProp> props,Select select) {
		SelectBuilder sb = Select.builder();
		
		for (SelectItem selectProp : select.listItems()) {
			FieldProp prop = CollectionUtils.findOne(props, p -> {
				return p.propName().equals(selectProp.prop());
			});
			// 只转换容器属性
			propBuildSelect(prop,selectProp,sb);
		}
		
		return sb.build();
	}
	
	// 构建模型直接归属的容器的select语句
	private static void propBuildSelect(FieldProp ctnProp,SelectItem selectProp,
		SelectBuilder selectBuilder) {
		
		selectBuilder.props(ctnProp.ctnField().fieldName());
		if(!ctnProp.isChildPropsEmpty() && !selectProp.isChildEmpty()) {
			for (SelectItem childSlcProp : selectProp.childs().listItems()) {
				FieldProp cp = ctnProp.getChildPropByName(childSlcProp.prop());
				selectBuilder.prop(ctnProp.ctnField().fieldName(),sb -> {
					propBuildSelect(cp,childSlcProp,sb);
				});
			}
		}
	}
	
	private static String modelCtnFrom(String ctnName,JSONQL ql) {
		return ctnName;
	}
	
	private static Where modelCtnWhere(List<? extends FieldProp> props,JSONQL jql) {
		Where where = jql.where();
		if(where == null) {return null;}
		
		if(where.keyIsProp()) {
			String wherePropName = where.keyAsProp();
			FieldProp prop = CollectionUtils.findOne(props, p-> {
				return p.propName().equals(wherePropName);
			});
			return replacePropWhere(prop,where);
		}
		
		else if(where.keyIsLogic()){
			List<Where> logicWheres = where.valAsWheres();
			List<Where> loginFieldWheres = new LinkedList<>();
			
			for (Where propWhere : logicWheres) {
				String wherePropName = propWhere.keyAsProp();
				FieldProp prop = CollectionUtils.findOne(props, p-> {
					return p.propName().equals(wherePropName);
				});
				if(prop instanceof CtnProp) {
					loginFieldWheres.add(replacePropWhere(prop,propWhere));
				}
			}
			
			if(where.keyIsAnd()) {
				return Where.and(loginFieldWheres);
			}
			else {
				return Where.or(loginFieldWheres);
			}
		}
		
		else {
			throw new ExecutException("错误的where语句{}",where.toJSONQL());
		}
	}
	
	/**
	 * 替换属性的where语句
	 * @param ctnProp 属性
	 * @param propWhere 以属性名称为key的where语句
	 * @return
	 */
	private static Where replacePropWhere(FieldProp prop,Where propWhere) {
		// 不是模型关联表的直接属性
		// 相当于没有搜索条件
		if(!(prop instanceof CtnProp)) {
			return null;
		}
		CtnProp ctnProp = (CtnProp)prop;
		Where fieldWhere = propWhere.replaceProp(ctnProp.ctnField().fieldName());
		
		// 递归替换子属性字段名称
		Where propWhereVal = propWhere.valAsWhere();
		if(!ctnProp.isChildPropsEmpty() && 
			(propWhereVal.keyIsProp() || propWhereVal.keyIsLogic())) {
			
			if(propWhereVal.keyIsProp()) {
				FieldProp childCtnProp = ctnProp.getChildPropByName(
					propWhereVal.keyAsProp());
				Where childFieldWhere = replacePropWhere(childCtnProp,propWhereVal);
				
				fieldWhere = fieldWhere.replaceVal(childFieldWhere);
			}
			
			else if(propWhereVal.keyIsLogic()) {
				List<Where> childFieldWheres = new LinkedList<>();
				for (Where childPropWhere : propWhereVal.valAsWheres()) {
					FieldProp childCtnProp = ctnProp.getChildPropByName(
							childPropWhere.keyAsProp());
					Where childFieldWhere = replacePropWhere(
							childCtnProp,childPropWhere);
					
					childFieldWheres.add(childFieldWhere);
				}
				
				
				if(propWhereVal.keyIsAnd()) {
					Where andWheres = Where.and(childFieldWheres);
					fieldWhere = fieldWhere.replaceVal(andWheres);
				}
				else {
					Where andWheres = Where.or(childFieldWheres);
					fieldWhere = fieldWhere.replaceVal(andWheres);
				}
			}
		}
		
		return fieldWhere;
	}
	
	private static Limit modelCtnLimit(String ctnName,List<? extends FieldProp> props, JSONQL jql) {
		Limit limit = jql.limit();
		if(limit == null) {return null;}
		
		Limit ctnLimit = limit.repChilds(new LinkedList<>())
			.repModel(ctnName);
		return ctnLimit;
	}
	
	private static Sort modelCtnSort(List<? extends FieldProp> props, JSONQL jql) {
		Sort sort = jql.sort();
		if(sort == null || sort.isEmpty()) {return null;}
		
		List<PropSort> propSorts = sort.propSorts();
		List<PropSort> fieldSorts = new LinkedList<>();
		for (PropSort propSort : propSorts) {
			FieldProp prop = CollectionUtils.findOne(props, p-> {
				return p.propName().equals(propSort.prop());
			});
			if(prop instanceof CtnProp) {
				fieldSorts.add(propSortToFieldSort((CtnProp)prop,propSort));
			}
		}
		
		return Sort.linkPropSorts(fieldSorts);
	}
	
	
	private static PropSort propSortToFieldSort(FieldProp ctnProp,PropSort propSort) {
		PropSort fieldSort = propSort.repProp(ctnProp.ctnField().fieldName());
		
		if(propSort.valIsSort()) {
			List<PropSort> childFieldSorts = new LinkedList<>();
			List<PropSort> childPropSorts = propSort.valAsSort().propSorts();
			
			for (PropSort childPropSort : childPropSorts) {
				FieldProp childCtnProp = ctnProp.getChildPropByName(childPropSort.prop());
				childFieldSorts.add(propSortToFieldSort(childCtnProp,childPropSort));
			}
			
			fieldSort = fieldSort.repVal(Sort.linkPropSorts(childFieldSorts));
		}
		
		return fieldSort;
	}
}
